package dk.aau.walkietalkie.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

import android.util.Log;
import dk.aau.walkietalkie.database.DBController;
import dk.aau.walkietalkie.database.Identification;
import dk.aau.walkietalkieui.IDBUpdateNotifier;
import dk.aau.walkietalkieui.WalkieTalkieApplication;

public class UDPBroadcastReceiver implements Runnable {

	// The port the UDP broadcast receiver listens on
	// public value to be used in the Sender!
	public static final int RECEIVER_PORT = 8175;
	// Wait infinitely for a new broadcast packet when set to 0
	public static final int SOCKET_TIMEOUT = 0; // ms

	private static final String DEBUG_TAG = "BroadcastReceiver";

	// Interface for calling the UI update function after updating the DB
	private IDBUpdateNotifier mTarget;
	private boolean mStopSignal;
	private String mOwnIP;

	// Pass an activity/fragment that implements the IDBUpdateNotifier interface
	// for updating the UI after the DB was modified
	public UDPBroadcastReceiver(IDBUpdateNotifier uiHandler) {
		this.mStopSignal = false;
		this.mTarget = uiHandler;
		updateOwnIP();
	}
	
	public void updateOwnIP() {
		mOwnIP = NetworkHelper.getDottedDecimalIP(NetworkHelper
				.getLocalIPAddress());
	}

	// Ends the main loop of the thread
	public void stopListening() {
		mStopSignal = true;
	}
		
	public void setUpdater(IDBUpdateNotifier updater) {
		mTarget = updater;
	}

	@Override
	public void run() {
		DatagramSocket serverSocket = null;
		mStopSignal = false;

		try {
			serverSocket = new DatagramSocket(RECEIVER_PORT);
			// serverSocket.setSoTimeout(SOCKET_TIMEOUT);
			Log.d(DEBUG_TAG, "Created server socket " + mOwnIP + ":"
					+ RECEIVER_PORT);
		} catch (SocketException e) {
			Log.e(DEBUG_TAG,
					"Could not create server socket: " + e.getMessage());
			e.printStackTrace();
		}

		while (!mStopSignal) {
			byte[] receiveData = new byte[UDPBroadcastSender.MAX_PACKET_SIZE];
			DatagramPacket receivePacket = new DatagramPacket(receiveData,
					receiveData.length);

			try {
				// Wait for a new packet for SOCKET_TIMEOUT ms
				serverSocket.receive(receivePacket);
				Log.v(DEBUG_TAG, "Received packet from"
						+ receivePacket.getAddress().getHostAddress());
				// Check the packet contents
				handlePacket(receivePacket.getData(), receivePacket.getAddress(), receivePacket.getPort());
			} catch (IOException e) {
				Log.e(DEBUG_TAG, "Receiving broadcast packet failed: " + e.getMessage());
				e.printStackTrace();
				// Close the server socket
				serverSocket.close();
				Log.d(DEBUG_TAG, "Closed server socket on " + mOwnIP);
				serverSocket = null;
			}
		}

		if (serverSocket != null) {
			serverSocket.close();
			Log.d(DEBUG_TAG, "Closed server socket on " + mOwnIP);
		}

	}

	protected void handlePacket(byte[] data, InetAddress ip, int port) {
		if (ip.getHostAddress().contains(mOwnIP) == false) {
			// Convert packet into string
			String recvStr = new String(data);
			Log.v(DEBUG_TAG, "Received broadcast form " + ip.toString() + " : "
					+ recvStr);

			int begPos = recvStr.lastIndexOf(UDPBroadcastSender.BROADCAST_TOKEN)
					+ UDPBroadcastSender.BROADCAST_TOKEN.length();
			int endPos = recvStr.indexOf(0, begPos); // end of received string

			// Correct packet received
			if (begPos != -1 && endPos != -1) {
				// Get the name from the packet string
				String name = recvStr.substring(begPos, endPos);
				String ipStr = ip.getHostAddress();
				
				DBController controller = new DBController(
						WalkieTalkieApplication.getContext());

				// create or update IP table and reset the alive counter value
				// to zero
				Identification identification = new Identification(
						ipStr, name, 0);
				controller.updateRow(identification);
				controller.close();
				Log.d(DEBUG_TAG, ">>> Announcement received: " + mOwnIP + " : " + ipStr + " is now known as " + name);

				// notify UI of database update
				mTarget.UpdateUI();
			}
		}
	}
}
